<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Nibbles in Qyoto</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="GUI, Qt, Nibbles, Qyoto, C#, learn Qyoto">
<meta name="description" content="In this chapter of the Qyoto tutorial, we will
create a Nibbles game clone.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Nibbles</h1>

<p>
In this part of the Qyoto C# programming tutorial, we will create a Nibbles game clone.
</p>

<div class="center">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* top_horizontal */
google_ad_slot = "3327173442";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p>
<code>Nibbles</code> is an older classic video game. It was first created in late 70s.
Later it was brought to PCs. In this game the player controls a snake.
The objective is to eat as many apples as possible. Each time the snake eats an apple,
its body grows. The snake must avoid the walls and its own body. 
</p>


<h2>Development</h2>

<p>
The size of each of the joints of a snake is 10px. The snake is controlled with 
the cursor keys. Initially, the snake has three joints. The game starts immediately. 
When the game is finished, we display "Game Over" message in the center of the window.
</p>

<div class="codehead">board.cs</div>
<pre class="code">
using System;
using QtCore;
using QtGui;

public class Board : QFrame 
{
    const int WIDTH = 300;
    const int HEIGHT = 300;
    const int DOT_SIZE = 10;
    const int ALL_DOTS = 900;
    const int RAND_POS = 30;
    const int DELAY = 140;

    int[] x = new int[ALL_DOTS];
    int[] y = new int[ALL_DOTS];

    int dots;
    int apple_x;
    int apple_y;

    bool left = false;
    bool right = true;

    bool up = false;
    bool down = false;
    bool inGame = true;

    QBasicTimer timer;
    QImage ball;
    QImage apple;
    QImage head;


    public Board() 
    {        
        StyleSheet = "QWidget { background-color: black }";

        FocusPolicy = Qt.FocusPolicy.StrongFocus;

        ball = new QImage("dot.png");
        apple = new QImage("apple.png");
        head = new QImage("head.png");
        
        InitGame();
    }


    void InitGame() 
    {
        dots = 3;

        for (int z = 0; z &lt; dots; z++) {
            x[z] = 50 - z*10;
            y[z] = 50;
        }

        LocateApple();

        timer = new QBasicTimer();
        timer.Start(DELAY, this);
    }


    protected override void OnPaintEvent(QPaintEvent e)
    {
        QPainter painter = new QPainter();
        painter.Begin(this);

        if (inGame) 
        {
            DrawObjects(painter);
        } else {
            GameOver(painter);
        }

        painter.End();
    }

    void DrawObjects(QPainter painter) 
    {
        painter.DrawImage(apple_x, apple_y, apple);

        for (int z = 0; z &lt; dots; z++) 
        {
            if (z == 0)
                painter.DrawImage(x[z], y[z], head);
            else painter.DrawImage(x[z], y[z], ball);
        }
    }

    void GameOver(QPainter painter) 
    {
        String msg = "Game Over";
        QFont small = new QFont("Helvetica", 12,
            (int) QFont.Weight.Bold);
        QFontMetrics metr = new QFontMetrics(small);
        
        int textWidth = metr.Width(msg);
        int h = Height;
        int w = Width;

        painter.SetPen(GlobalColor.white);
        painter.Font = small;
        painter.Translate(new QPoint(w/2, h/2));
        painter.DrawText(-textWidth/2, 0, msg);
    }


    void CheckApple() 
    {
        if ((x[0] == apple_x) &amp;&amp; (y[0] == apple_y)) 
        {
            dots++;
            LocateApple();
        }
    }

    void Move() 
    {
        for (int z = dots; z > 0; z--) 
        {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (left) 
        {
            x[0] -= DOT_SIZE;
        }

        if (right) 
        {
            x[0] += DOT_SIZE;
        }

        if (up)
        {
            y[0] -= DOT_SIZE;
        }

        if (down) 
        {
            y[0] += DOT_SIZE;
        }
    }


    void CheckCollision() 
    {
        for (int z = dots; z > 0; z--) 
        {
            if ((z > 4) &amp;&amp; (x[0] == x[z]) &amp;&amp; (y[0] == y[z])) 
            {
                inGame = false;
            }
        }

        if (y[0] > HEIGHT) 
        {
            inGame = false;
        }

        if (y[0] &lt; 0) 
        {
            inGame = false;
        }

        if (x[0] > WIDTH) 
        {
            inGame = false;
        }

        if (x[0] &lt; 0) 
        {
            inGame = false;
        }
    }

    void LocateApple() 
    {
        Random rand = new Random();

        int r = (int) (rand.Next(RAND_POS));
        apple_x = ((r * DOT_SIZE));
        r = (int) (rand.Next(RAND_POS));
        apple_y = ((r * DOT_SIZE));
    }

    protected override void OnTimerEvent(QTimerEvent arg1) 
    {
        if (inGame) 
        {
            CheckApple();
            CheckCollision();
            Move();
        } else 
        {
            timer.Stop();
        }

        Repaint();
    }


    protected override void OnKeyPressEvent(QKeyEvent e)
    {        
        int key = e.Key();
        
        if (key == (int) Qt.Key.Key_Left &amp;&amp; !right) 
        {
            left = true;
            up = false;
            down = false;
        }
        
        if ((key == (int) Qt.Key.Key_Right) &amp;&amp; !left) 
        {
            right = true;
            up = false;
            down = false;            
        }
        
        if ((key == (int) Qt.Key.Key_Up) &amp;&amp; !down) 
        {
            up = true;
            right = false;
            left = false;            
        }
        
        if ((key == (int) Qt.Key.Key_Down) &amp;&amp; !up) 
        {
            down = true;
            right = false;
            left = false;       
        }
    }
}
</pre>

<p>
First we will define some globals used in our game. 
</p>

<p>
The <code>WIDTH</code> and <code>HEIGHT</code> constants determine 
the size of the Board. The <code>DOT_SIZE</code> is the size of the apple and the dot
of the snake. The <code>ALL_DOTS</code> constant defines the maximum number of 
possible dots on the Board.
The <code>RAND_POS</code> constant is used to calculate a random position of an apple. 
The <code>DELAY</code> constant determines the speed of the game.
</p>

<pre class="explanation">
int[] x = new int[ALL_DOTS];
int[] y = new int[ALL_DOTS];
</pre>

<p>
These two arrays store x, y coordinates of all possible joints of a snake. 
</p>

<p>
The <code>InitGame()</code> method initializes variables, loads 
images and starts a timeout function.
</p>

<pre class="explanation">
if (inGame) 
{
    DrawObjects(painter);
} else {
    GameOver(painter);
}
</pre>

<p>
Inside the <code>PaintEvent()</code> method, we check the <code>inGame</code>
variable. If it is true, we draw our objects. The apple and the snake joints. 
Otherwise we display "Game over" text.  
</p>

<pre class="explanation">
void DrawObjects(QPainter painter) 
{
    painter.DrawImage(apple_x, apple_y, apple);

    for (int z = 0; z &lt; dots; z++) 
    {
        if (z == 0)
            painter.DrawImage(x[z], y[z], head);
        else painter.DrawImage(x[z], y[z], ball);
    }
}
</pre>

<p>
The <code>DrawObjects()</code> method draws the apple and the joints of the
snake. The first joint of a snake is its head, which is represented 
by a red circle. 
</p>

<pre class="explanation">
void CheckApple() 
{
    if ((x[0] == apple_x) &amp;&amp; (y[0] == apple_y)) 
    {
        dots++;
        LocateApple();
    }
}
</pre>

<p>
The <code>CheckApple()</code> method checks, if the snake has hit
the apple object. If so, we add another snake joint and call the 
<code>LocateApple()</code> method, which randomly places a new apple object. 
</p>

<p>
In the <code>Move()</code> method we have the key algorithm of the game. 
To understand it, look at how the snake is moving. You control the head of the snake. 
You can change its direction with the cursor keys. The rest of the joints move
one position up the chain. The second joint moves where the first was, 
the third joint where the second was etc. 
</p>

<pre class="explanation">
for (int z = dots; z > 0; z--)
{
    x[z] = x[(z - 1)];
    y[z] = y[(z - 1)];
}
</pre>

<p>
This code moves the joints up the chain. 
</p>

<pre class="explanation">
if (left) 
{
    x[0] -= DOT_SIZE;
}
</pre>

<p>
Move the head to the left.
</p>

<p>
In the <code>CheckCollision()</code> method, we determine if the snake
has hit itself or one of the walls. 
</p>


<pre class="explanation">
for (int z = dots; z > 0; z--) 
{
    if ((z > 4) &amp;&amp; (x[0] == x[z]) &amp;&amp; (y[0] == y[z])) 
    {
        inGame = false;
    }
}
</pre>

<p>
Finish the game, if the snake hits one of its joints with the head.
</p>

<pre class="explanation">
if (y[0] > HEIGHT) 
{
    inGame = false;
}
</pre>

<p>
Finish the game, if the snake hits the bottom of the Board. 
</p>

<p>
The <code>LocateApple()</code> method locates an apple randomly 
on the board.
</p>

<pre class="explanation">
Random rand = new Random();

int r = (int) (rand.Next(RAND_POS));
</pre>

<p>
We get a random number from 0 to RAND_POS - 1. 
</p>

<pre class="explanation">
apple_x = ((r * DOT_SIZE));
...
apple_y = ((r * DOT_SIZE));
</pre>

<p>
These line set the x, y coordinates of the apple
object. 
</p>

<pre class="explanation">
if (inGame) 
{
    CheckApple();
    CheckCollision();
    Move();
} else 
{
    timer.Stop();
}
</pre>

<p>
Every 140 ms, the <code>TimerEvent()</code> method is called. If 
we are in the game, we call three methods, that build the logic of the game.
Otherwise we stop the timer. 
</p>


<p>
In the <code>KeyPressEvent()</code> method of the Board class, we 
determine the keys that were pressed.
</p>

<pre class="explanation">
if (key == (int) Qt.Key.Key_Left &amp;&amp; !right) 
{
    left = true;
    up = false;
    down = false;
}
</pre>

<p>
If we hit the left cursor key, we set <code>left</code> variable to 
true. This variable is used in the <code>Move()</code>
method to change coordinates of the snake object. Notice also, that
when the snake is heading to the right, we cannot turn immediately 
to the left. 
</p>

<div class="codehead">nibbles.cs</div>
<pre class="code">
using System;
using QtCore;
using QtGui;

/**
 * ZetCode Qyoto C# tutorial
 *
 * In this program, we create
 * a Nibbles game clone.
 *
 * @author Jan Bodnar
 * website zetcode.com
 * last modified October 2012
 */


public class QyotoApp : QMainWindow 
{    
    public QyotoApp() 
    {
        WindowTitle = "Nibbles";

        CentralWidget= new Board();

        Resize(310, 310);
        Move(300, 300);
        Show();
    }

    [STAThread]
    public static int Main(String[] args) 
    {
        new QApplication(args);
        new QyotoApp();

        return QApplication.Exec();
    }
}
</pre>

<p>
In this class, we set up the Nibbles game. 
</p>


<img src="/img/gui/sharpqyoto/nibbles.png" alt="Nibbles">
<div class="figure">Figure: Nibbles</div>

<p>
This was the Nibbles computer game programmed with the Qyoto 
library and the C# programming language.
</p>

<div class="center">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<br>

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified November 1, 2012  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>
